/****************************************************************************
*
* Random Dungeon Mapper, from Netherworld Dreamscape
*
* Written by Morpheus Nosferatu/Jeff Standish
* morpheus@sage.cc.purdue.edu
* standish@mentor.cc.purdue.edu
*
* Initial version, March 1993
*
* This program copyright by Jeff Standish, 1993. All rights reserved. You
* are free to copy and modify this program, so long as this header remains
* intact and appropriate attribution is given to the author. This program
* may not be sold for profit without the author's consent.
*
* Compile this program with you favorite C compiler and run it with the
* -help or -h option, which will give usage information for the command
* line options.
*
* This program generates random dungeon maps, printing out LaTeX [default]
* or PostScript files. For each level of the dungeon, a different file is
* generated by taking the base file name and appending the level of the
* dungeon and the appropriate format suffix [.tex or .ps]. Files can then
* be displayed on a graphics terminal viewer or sent to a laser printer
* capable of graphics (in the case of LaTeX files, after the .tex files
* have been converted to appropriate printable format by a "latex" program).
*
* The size of dungeon can be scaled, preferably in the range of 100 to 500.
* This scale is the maximum width of the dungeon in feet. Since this program
* treates graphical coordinates in points (1 point = 1/72 inch), the maximum
* good resolution of a dungeon will be with a width of 500. If the size is
* set larger than this, the lines will overlap and make the map difficult to
* read (even a map of size 500 is somewhat difficult to read due to how small
* it is).
*
* The entrance to the dungeon is either a doorway or a stairway. The doorway
* will be placed in the middle of the wall such that the door faces in a
* given direction (a door facing north is in the south wall of the dungeon).
* If the entrance is a stairway, it is placed in the middle of the first
* level of the dungeon.
*
****************************************************************************/
#include
#define MAX(a,b) ((a > b) ? (a) : (b))
#define MIN(a,b) ((a < b) ? (a) : (b))
#define EXITSTACK 0
#define ROOM 1
#define CHAMBER 2
#define PASSAGE 3
#define JUNCTION 4
#define STAIRWAY 5
#define DOORWAY 10
#define ARCHWAY 11
#define OPENING 12
#define NORTH 20
#define SOUTH 21
#define EAST 22
#define WEST 23
#define UP 24
#define DOWN 25
#define XSIZE 500
#define YSIZE 700
#define PRINTLATEX 1
#define PRINTPS 2
struct queuestruct {
struct exitstruct *exit;
struct queuestruct *queuenext;
};
struct exitstruct {
int orientation, x1, y1, x2, y2, exittype, locationtype, level;
struct locationstruct *parent;
struct exitstruct *next;
};
struct linestruct {
int x1, y1, x2, y2;
struct linestruct *next;
};
struct locationstruct {
int xmin, xmax, ymin, ymax, locationtype, locationdata, level, number;
struct linestruct *firstline;
struct locationstruct *next, *previous;
};
typedef struct queuestruct QUEUE;
typedef struct exitstruct EXITS;
typedef struct linestruct LINES;
typedef struct locationstruct LOCAT;
static int stairwaydownlines5[4][11][4] = {
{ { 0, 20, 5, 20 },
{ 0, 0, 0, 20 },
{ 5, 0, 5, 20 },
{ 0, 4, 5, 4 },
{ 0, 6, 5, 6 },
{ 1, 8, 4, 8 },
{ 1, 10, 4, 10 },
{ 1, 12, 4, 12 },
{ 2, 14, 3, 14 },
{ 2, 16, 3, 16 },
{ 2, 18, 3, 18 } },
{ { 0,-20, -5,-20 },
{ 0, 0, 0,-20 },
{ -5, 0, -5,-20 },
{ 0, -4, -5, -4 },
{ 0, -6, -5, -6 },
{ -1, -8, -4, -8 },
{ -1,-10, -4,-10 },
{ -1,-12, -4,-12 },
{ -2,-14, -3,-14 },
{ -2,-16, -3,-16 },
{ -2,-18, -3,-18 } },
{ { 20, 0, 20, -5 },
{ 0, 0, 20, 0 },
{ 0, -5, 20, -5 },
{ 4, 0, 4, -5 },
{ 6, 0, 6, -5 },
{ 8, -1, 8, -4 },
{ 10, -1, 10, -4 },
{ 12, -1, 12, -4 },
{ 14, -2, 14, -3 },
{ 16, -2, 16, -3 },
{ 18, -2, 18, -3 } },
{ {-20, 0,-20, 5 },
{ 0, 0,-20, 0 },
{ 0, 5,-20, 5 },
{ -4, 0, -4, 5 },
{ -6, 0, -6, 5 },
{ -8, 1, -8, 4 },
{-10, 1,-10, 4 },
{-12, 1,-12, 4 },
{-14, 2,-14, 3 },
{-16, 2,-16, 3 },
{-18, 2,-18, 3 } }
};
static int stairwayuplines5[4][11][4] = {
{ { 0, 20, 5, 20 },
{ 0, 0, 0, 20 },
{ 5, 0, 5, 20 },
{ 0, 18, 5, 18 },
{ 0, 16, 5, 16 },
{ 1, 14, 4, 14 },
{ 1, 12, 4, 12 },
{ 1, 10, 4, 10 },
{ 2, 8, 3, 8 },
{ 2, 6, 3, 6 },
{ 2, 4, 3, 4 } },
{ { 0,-20, -5,-20 },
{ 0, 0, 0,-20 },
{ -5, 0, -5,-20 },
{ 0,-18, -5,-18 },
{ 0,-16, -5,-16 },
{ -1,-14, -4,-14 },
{ -1,-12, -4,-12 },
{ -1,-10, -4,-10 },
{ -2, -8, -3, -8 },
{ -2, -6, -3, -6 },
{ -2, -4, -3, -4 } },
{ { 20, 0, 20, -5 },
{ 0, 0, 20, 0 },
{ 0, -5, 20, -5 },
{ 18, 0, 18, -5 },
{ 16, 0, 16, -5 },
{ 14, -1, 14, -4 },
{ 12, -1, 12, -4 },
{ 10, -1, 10, -4 },
{ 8, -2, 8, -3 },
{ 6, -2, 6, -3 },
{ 4, -2, 4, -3 } },
{ {-20, 0,-20, 5 },
{ 0, 0,-20, 0 },
{ 0, 5,-20, 5 },
{-18, 0,-18, 5 },
{-16, 0,-16, 5 },
{-14, 1,-14, 4 },
{-12, 1,-12, 4 },
{-10, 1,-10, 4 },
{ -8, 2, -8, 3 },
{ -6, 2, -6, 3 },
{ -4, 2, -4, 3 } }
};
static int stairwaydownlines10[4][11][4] = {
{ { 0, 20, 10, 20 },
{ 0, 0, 0, 20 },
{ 10, 0, 10, 20 },
{ 1, 4, 9, 4 },
{ 1, 6, 9, 6 },
{ 2, 8, 8, 8 },
{ 2, 10, 8, 10 },
{ 3, 12, 7, 12 },
{ 3, 14, 7, 14 },
{ 4, 16, 6, 16 },
{ 4, 18, 6, 18 } },
{ { 0,-20,-10,-20 },
{ 0, 0, 0,-20 },
{-10, 0,-10,-20 },
{ -1, -4, -9, -4 },
{ -1, -6, -9, -6 },
{ -2, -8, -8, -8 },
{ -2,-10, -8,-10 },
{ -3,-12, -7,-12 },
{ -3,-14, -7,-14 },
{ -4,-16, -6,-16 },
{ -4,-18, -6,-18 } },
{ { 20, 0, 20,-10 },
{ 0, 0, 20, 0 },
{ 0,-10, 20,-10 },
{ 4, -1, 4, -9 },
{ 6, -1, 6, -9 },
{ 8, -2, 8, -8 },
{ 10, -2, 10, -8 },
{ 12, -3, 12, -7 },
{ 14, -3, 14, -7 },
{ 16, -4, 16, -6 },
{ 18, -4, 18, -6 } },
{ {-20, 0,-20, 10 },
{ 0, 0,-20, 0 },
{ 0, 10,-20, 10 },
{ -4, 1, -4, 9 },
{ -6, 1, -6, 9 },
{ -8, 2, -8, 8 },
{-10, 2,-10, 8 },
{-12, 3,-12, 7 },
{-14, 3,-14, 7 },
{-16, 4,-16, 6 },
{-18, 4,-18, 6 } }
};
static int stairwayuplines10[4][11][4] = {
{ { 0, 20, 10, 20 },
{ 0, 0, 0, 20 },
{ 10, 0, 10, 20 },
{ 1, 18, 9, 18 },
{ 1, 16, 9, 16 },
{ 2, 14, 8, 14 },
{ 2, 12, 8, 12 },
{ 3, 10, 7, 10 },
{ 3, 8, 7, 8 },
{ 4, 6, 6, 6 },
{ 4, 4, 6, 4 } },
{ { 0,-20,-10,-20 },
{ 0, 0, 0,-20 },
{-10, 0,-10,-20 },
{ -1,-18, -9,-18 },
{ -1,-16, -9,-16 },
{ -2,-14, -8,-14 },
{ -2,-12, -8,-12 },
{ -3,-10, -7,-10 },
{ -3, -8, -7, -8 },
{ -4, -6, -6, -6 },
{ -4, -4, -6, -4 } },
{ { 20, 0, 20,-10 },
{ 0, 0, 20, 0 },
{ 0,-10, 20,-10 },
{ 18, -1, 18, -9 },
{ 16, -1, 16, -9 },
{ 14, -2, 14, -8 },
{ 12, -2, 12, -8 },
{ 10, -3, 10, -7 },
{ 8, -3, 8, -7 },
{ 6, -4, 6, -6 },
{ 4, -4, 4, -6 } },
{ {-20, 0,-20, 10 },
{ 0, 0,-20, 0 },
{ 0, 10,-20, 10 },
{-18, 1,-18, 9 },
{-16, 1,-16, 9 },
{-14, 2,-14, 8 },
{-12, 2,-12, 8 },
{-10, 3,-10, 7 },
{ -8, 3, -8, 7 },
{ -6, 4, -6, 6 },
{ -4, 4, -4, 6 } }
};
static int stairwaydownlines15[4][11][4] = {
{ { 0, 20, 15, 20 },
{ 0, 0, 0, 20 },
{ 15, 0, 15, 20 },
{ 0, 4, 15, 4 },
{ 1, 6, 14, 6 },
{ 2, 8, 13, 8 },
{ 3, 10, 12, 10 },
{ 4, 12, 11, 12 },
{ 5, 14, 10, 14 },
{ 6, 16, 9, 16 },
{ 7, 18, 8, 18 } },
{ { 0,-20,-15,-20 },
{ 0, 0, 0,-20 },
{-15, 0,-15,-20 },
{ 0, -4,-15, -4 },
{ -1, -6,-14, -6 },
{ -2, -8,-13, -8 },
{ -3,-10,-12,-10 },
{ -4,-12,-11,-12 },
{ -5,-14,-10,-14 },
{ -6,-16, -9,-16 },
{ -7,-18, -8,-18 } },
{ { 20, 0, 20,-15 },
{ 0, 0, 20, 0 },
{ 0,-15, 20,-15 },
{ 4, 0, 4,-15 },
{ 6, -1, 6,-14 },
{ 8, -2, 8,-13 },
{ 10, -3, 10,-12 },
{ 12, -4, 12,-11 },
{ 14, -5, 14,-10 },
{ 16, -6, 16, -9 },
{ 18, -7, 18, -8 } },
{ {-20, 0,-20, 15 },
{ 0, 0,-20, 0 },
{ 0, 15,-20, 15 },
{ -4, 0, -4, 15 },
{ -6, 1, -6, 14 },
{ -8, 2, -8, 13 },
{-10, 3,-10, 12 },
{-12, 4,-12, 11 },
{-14, 5,-14, 10 },
{-16, 6,-16, 9 },
{-18, 7,-18, 8 } }
};
static int stairwayuplines15[4][11][4] = {
{ { 0, 20, 15, 20 },
{ 0, 0, 0, 20 },
{ 15, 0, 15, 20 },
{ 0, 18, 15, 18 },
{ 1, 16, 14, 16 },
{ 2, 14, 13, 14 },
{ 3, 12, 12, 12 },
{ 4, 10, 11, 10 },
{ 5, 8, 10, 8 },
{ 6, 6, 9, 6 },
{ 7, 4, 8, 4 } },
{ { 0,-20,-15,-20 },
{ 0, 0, 0,-20 },
{-15, 0,-15,-20 },
{ 0,-18,-15,-18 },
{ -1,-16,-14,-16 },
{ -2,-14,-13,-14 },
{ -3,-12,-12,-12 },
{ -4,-10,-11,-10 },
{ -5, -8,-10, -8 },
{ -6, -6, -9, -6 },
{ -7, -4, -8, -4 } },
{ { 20, 0, 20,-15 },
{ 0, 0, 20, 0 },
{ 0,-15, 20,-15 },
{ 18, 0, 18,-15 },
{ 16, -1, 16,-14 },
{ 14, -2, 14,-13 },
{ 12, -3, 12,-12 },
{ 10, -4, 10,-11 },
{ 8, -5, 8,-10 },
{ 6, -6, 6, -9 },
{ 4, -7, 4, -8 } },
{ {-20, 0,-20, 15 },
{ 0, 0,-20, 0 },
{ 0, 15,-20, 15 },
{-18, 0,-18, 15 },
{-16, 1,-16, 14 },
{-14, 2,-14, 13 },
{-12, 3,-12, 12 },
{-10, 4,-10, 11 },
{ -8, 5, -8, 10 },
{ -6, 6, -6, 9 },
{ -4, 7, -4, 8 } }
};
static int doorwaylines5[4][11][4] = {
{ { 0, 0, 1, 0 },
{ 0, 1, 1, 1 },
{ 4, 0, 5, 0 },
{ 4, 1, 5, 1 },
{ 1, -1, 4, -1 },
{ 1, 2, 4, 2 },
{ 1, -1, 1, 2 },
{ 4, -1, 4, 2 } },
{ { 0, 0, -1, 0 },
{ 0, -1, -1, -1 },
{ -4, 0, -5, 0 },
{ -4, -1, -5, -1 },
{ -1, 1, -4, 1 },
{ -1, -2, -4, -2 },
{ -1, 1, -1, -2 },
{ -4, 1, -4, -2 } },
{ { 0, 0, 0, -1 },
{ 1, 0, 1, -1 },
{ 0, -4, 0, -5 },
{ 1, -4, 1, -5 },
{ -1, -1, -1, -4 },
{ 2, -1, 2, -4 },
{ -1, -1, 2, -1 },
{ -1, -4, 2, -4 } },
{ { 0, 0, 0, 1 },
{ -1, 0, -1, 1 },
{ 0, 4, 0, 5 },
{ -1, 4, -1, 5 },
{ 1, 1, 1, 4 },
{ -2, 1, -2, 4 },
{ 1, 1, -2, 1 },
{ 1, 4, -2, 4 } }
};
static int doorwaylines10[4][8][4] = {
{ { 0, 0, 2, 0 },
{ 0, 1, 2, 1 },
{ 8, 0, 10, 0 },
{ 8, 1, 10, 1 },
{ 2, -1, 8, -1 },
{ 2, 2, 8, 2 },
{ 2, -1, 2, 2 },
{ 8, -1, 8, 2 } },
{ { 0, 0, -2, 0 },
{ 0, -1, -2, -1 },
{ -8, 0,-10, 0 },
{ -8, -1,-10, -1 },
{ -2, 1, -8, 1 },
{ -2, -2, -8, -2 },
{ -2, 1, -2, -2 },
{ -8, 1, -8, -2 } },
{ { 0, 0, 0, -2 },
{ 1, 0, 1, -2 },
{ 0, -8, 0,-10 },
{ 1, -8, 1,-10 },
{ -1, -2, -1, -8 },
{ 2, -2, 2, -8 },
{ -1, -2, 2, -2 },
{ -1, -8, 2, -8 } },
{ { 0, 0, 0, 2 },
{ -1, 0, -1, 2 },
{ 0, 8, 0, 10 },
{ -1, 8, -1, 10 },
{ 1, 2, 1, 8 },
{ -2, 2, -2, 8 },
{ 1, 2, -2, 2 },
{ 1, 8, -2, 8 } }
};
static int doorwaylines15[4][9][4] = {
{ { 0, 0, 3, 0 },
{ 0, 1, 3, 1 },
{ 12, 0, 15, 0 },
{ 12, 1, 15, 1 },
{ 3, -1, 12, -1 },
{ 3, 2, 12, 2 },
{ 3, -1, 3, 2 },
{ 7, -1, 7, 2 },
{ 12, -1, 12, 2 } },
{ { 0, 0, -3, 0 },
{ 0, -1, -3, -1 },
{-12, 0,-15, 0 },
{-12, -1,-15, -1 },
{ -3, 1,-12, 1 },
{ -3, -2,-12, -2 },
{ -3, 1, -3, -2 },
{ -7, 1, -7, -2 },
{-12, 1,-12, -2 } },
{ { 0, 0, 0, -3 },
{ 1, 0, 1, -3 },
{ 0,-12, 0,-15 },
{ 1,-12, 1,-15 },
{ -1, -3, -1,-12 },
{ 2, -3, 2,-12 },
{ -1, -3, 2, -3 },
{ -1, -7, 2, -7 },
{ -1,-12, 2,-12 } },
{ { 0, 0, 0, 3 },
{ -1, 0, -1, 3 },
{ 0, 12, 0, 15 },
{ -1, 12, -1, 15 },
{ 1, 3, 1, 12 },
{ -2, 3, -2, 12 },
{ 1, 3, -2, 3 },
{ 1, 7, -2, 7 },
{ 1, 12, -2, 12 } }
};
#define LATEXHEADERLEN 10
static char *latexheader[LATEXHEADERLEN] = {
"\\documentstyle{report}",
"\\pagestyle{empty}",
"\\addtolength{\\textwidth}{3in}",
"\\addtolength{\\oddsidemargin}{-1.5in}",
"\\addtolength{\\topmargin}{-1.5in}",
"\\addtolength{\\textheight}{3in}",
"",
"\\begin{document}",
"\\begin{picture}(500,730)",
""
};
#define HELPLEN 18
static char *helpmessage[HELPLEN] = {
" -help\t\t - this help message",
" -entrancedirection [direction] - select the direction that",
"\t\t\t\t the entrance to the first level is facing,",
"\t\t\t\t select from \"north\", \"south\", \"east\",",
"\t\t\t\t or \"west\", defaults to \"north\"",
" -entrancetype [type] - select form of entrance to first level,",
"\t\t\t\t select from \"doorway\" or \"stairway\"",
"\t\t\t\t defaults to \"stairway\"",
" -output [format]\t - set the map file format as either \"latex\"",
"\t\t\t\t or \"postscript\", defaults to \"latex\"",
" -maxlevel [level]\t - maximum level of the dungeon,",
"\t\t\t\t defaults to level = 1",
" -mapwidth [size]\t - maximum width of the dungeon, height = width * 1.4",
"\t\t\t\t defaults to size = 250",
"\t\t\t\t warning: strange maps may result if size > 500",
" -number\t\t - number the rooms on the map",
" -dubug\t\t - turn on debugging messages",
" filename\t\t - the base name of output file"
};
char filename[100];
int mapwidth = 250, mapheight = 350;
int entrancetype = STAIRWAY, entrancedirection = NORTH;
int startlevel = 1, maxlevel = 1;
int gc = 0, gcnum = 0, printout = PRINTLATEX, numberoutput = 0;
int debugging = 0;
EXITS *exitfreestack = NULL;
LOCAT *firstlocation = NULL, *lastlocation = NULL;
QUEUE *queuehead = NULL, *queuetail = NULL, *queuefreestack = NULL;
EXITS *new_exit_node();
EXITS *dequeue_exit();
LINES *new_line_node();
LOCAT *new_location_node();
LOCAT *box_empty();
QUEUE *new_queue_node();
/****************************************************************************
*
* main() - read in command line arguments and call the functions for
* generating and drawing the maps
*
****************************************************************************/
int main(argc, argv)
int argc;
char **argv;
{
int ac;
strcpy(filename, "map");
if (argc == 1) {
print_usage(argv[0]);
exit(0);
}
/* read in the various options */
ac = 1;
while (ac < argc) {
if (!strcmp(argv[ac], "-entrancetype")) {
++ac;
if (ac < argc) {
if (!strcmp(argv[ac], "stairway"))
entrancetype = STAIRWAY;
else if (!strcmp(argv[ac], "doorway"))
entrancetype = DOORWAY;
else
fatal_error("unknown entrance type \"%s\"", argv[ac]);
}
else
fatal_error("entrance type not given in command line");
}
else if (!strcmp(argv[ac], "-entrancedirection")) {
++ac;
if (ac < argc) {
if (!strcmp(argv[ac], "north"))
entrancedirection = NORTH;
else if (!strcmp(argv[ac], "south"))
entrancedirection = SOUTH;
else if (!strcmp(argv[ac], "east"))
entrancedirection = EAST;
else if (!strcmp(argv[ac], "west"))
entrancedirection = WEST;
else
fatal_error("invalid entrance direction \"%s\"", argv[ac]);
}
else
fatal_error("entrance direction not given in command line");
}
else if (!strcmp(argv[ac], "-maxlevel")) {
++ac;
if (ac < argc) {
maxlevel = atoi(argv[ac]);
if (maxlevel < 1)
fatal_error("invalid maximum level: %s", argv[ac]);
}
else
fatal_error("maximum level value not given");
}
else if (!strcmp(argv[ac], "-mapwidth")) {
++ac;
if (ac < argc) {
mapwidth = atoi(argv[ac]);
mapheight = mapwidth * 1.4;
}
else
fatal_error("maximum map size not given");
}
else if (!strcmp(argv[ac], "-output")) {
++ac;
if (ac < argc) {
if (!strcmp(argv[ac], "latex"))
printout = PRINTLATEX;
else if (!strcmp(argv[ac], "postscript"))
printout = PRINTPS;
else
fatal_error("unknown output format \"%s\"", argv[ac]);
}
else
fatal_error("output form not given");
}
else if (!strcmp(argv[ac], "-number")) {
numberoutput = 1;
}
else if (!strcmp(argv[ac], "-debug")) {
debugging = 1;
}
else if ((!strcmp(argv[ac], "-help")) || (!strcmp(argv[ac], "-h"))) {
print_usage(argv[0]);
exit(0);
}
else
break;
++ac;
}
/* get the root name for the output files */
if (ac < argc) {
if (argv[ac][0] == '-') {
printf("invalid option \"%s\"\n\n", argv[ac]);
print_usage(argv[0]);
exit(1);
}
else if (argc > (ac + 1)) {
printf("extra information after filename\n\n");
print_usage(argv[0]);
exit(1);
}
else
strcpy(filename, argv[ac]);
}
else {
printf("filename not given\n\n");
print_usage(argv[0]);
exit(1);
}
srand(time(0));
create_entrance();
process_exit_queue();
printf("\n\n");
stock_dungeon();
if (printout == PRINTPS)
print_postscript_file();
else
print_latex_file();
return 0;
}
/****************************************************************************
*
* print_usage() - print a short usage message and information on arguments
*
****************************************************************************/
print_usage(name)
char name[];
{
int i;
printf("Usage: %s [argument list] filename\n", name);
for (i = 0; i < HELPLEN; ++i)
printf("%s\n", helpmessage[i]);
}
/****************************************************************************
*
* stock_dungeon() - presently, this function merely scans through the dungeon
* and numbers the rooms and determines the maximum level
* that was generated
*
****************************************************************************/
stock_dungeon()
{
int lvl, count, roomnum;
LOCAT *room;
roomnum = 1;
for (lvl = 1; lvl <= maxlevel; ++lvl) {
count = 1;
room = firstlocation;
while (room != NULL) {
if (room->level == lvl) {
if (((digits_in_number(count) * 4)
<= (scale_x(room->xmax) - scale_x(room->xmin)))
&& ((room->locationtype == ROOM)
|| (room->locationtype == CHAMBER))) {
room->number = roomnum++;
}
++count;
}
room = room->next;
}
if (count == 1) {
maxlevel = lvl - 1;
break;
}
}
}
/****************************************************************************
*
* digits_in_number() - given an integer, return the number of digits in it
*
****************************************************************************/
int digits_in_number(foo)
int foo;
{
int i = 1;
while (foo > 9) {
foo /= 10;
++i;
}
return i;
}
/****************************************************************************
*
* create_entrance() - generate the entrance of the dungeon, either a stairway
* positioned in the middle of the first level, or a
* doorway in one of the walls (with the assumption
* that the first level is above ground)
*
****************************************************************************/
create_entrance()
{
int xpos, ypos;
LOCAT *room;
EXITS *exit;
room = new_location_node();
add_location_to_queue(room);
room->level = startlevel;
room->locationtype = STAIRWAY;
exit = new_exit_node();
add_exit_to_queue(exit);
exit->level = startlevel;
exit->orientation = entrancedirection;
exit->locationtype = ROOM;
exit->parent = room;
if (entrancetype == DOORWAY) {
exit->exittype = DOORWAY;
switch (entrancedirection) {
case NORTH:
exit->x1 = (mapwidth / 2) - 5;
exit->x2 = (mapwidth / 2) + 5;
exit->y1 = 1;
exit->y2 = 1;
add_line_to_location(room, (mapwidth / 2) - 5, 1,
(mapwidth / 2) - 5, 0);
add_line_to_location(room, (mapwidth / 2) + 5, 1,
(mapwidth / 2) + 5, 0);
add_line_to_location(room, 0, 0, (mapwidth / 2) - 5, 0);
add_line_to_location(room, mapwidth, 0, (mapwidth / 2) + 5, 0);
break;
case SOUTH:
exit->x1 = (mapwidth / 2) - 5;
exit->x2 = (mapwidth / 2) + 5;
exit->y1 = mapheight - 1;
exit->y2 = mapheight - 1;
add_line_to_location(room, (mapwidth / 2) - 5, mapheight - 1,
(mapwidth / 2) - 5, mapheight);
add_line_to_location(room, (mapwidth / 2) + 5, mapheight - 1,
(mapwidth / 2) + 5, mapheight);
add_line_to_location(room, 0, mapheight,
(mapwidth / 2) - 5, mapheight);
add_line_to_location(room, mapwidth, mapheight,
(mapwidth / 2) + 5, mapheight);
break;
case EAST:
exit->x1 = 1;
exit->x2 = 1;
exit->y1 = (mapheight / 2) - 5;
exit->y2 = (mapheight / 2) + 5;
add_line_to_location(room, 1, (mapheight / 2) - 5,
0, (mapheight / 2) - 5);
add_line_to_location(room, 1, (mapheight / 2) + 5,
0, (mapheight / 2) + 5);
add_line_to_location(room, 0, 0, 0, (mapheight / 2) - 5);
add_line_to_location(room, 0, mapheight, 0, (mapheight/2)+5);
break;
case WEST:
exit->x1 = mapwidth - 1;
exit->x2 = mapwidth - 1;
exit->y1 = (mapheight / 2) - 5;
exit->y2 = (mapheight / 2) + 5;
add_line_to_location(room, mapwidth - 1, (mapheight / 2) - 5,
mapwidth, (mapheight / 2) - 5);
add_line_to_location(room, mapwidth - 1, (mapheight / 2) + 5,
mapwidth, (mapheight / 2) + 5);
add_line_to_location(room, mapwidth, 0, mapwidth,
(mapheight / 2) - 5);
add_line_to_location(room, mapwidth, mapheight, mapwidth,
(mapheight/2)+5);
break;
default:
fatal_error("create_entrance(): unknown entrance direction");
}
if (entrancedirection != NORTH)
add_line_to_location(room, 0, 0, mapwidth, 0);
if (entrancedirection != SOUTH)
add_line_to_location(room, 0, mapheight, mapwidth, mapheight);
if (entrancedirection != EAST)
add_line_to_location(room, 0, 0, 0, mapheight);
if (entrancedirection != WEST)
add_line_to_location(room, mapwidth, 0, mapwidth, mapheight);
}
else if (entrancetype == STAIRWAY) {
xpos = mapwidth / 2;
ypos = mapheight / 2;
exit->x1 = xpos;
exit->y1 = ypos;
exit->x2 = xpos;
exit->y2 = ypos;
exit->exittype = OPENING;
switch (entrancedirection) {
case NORTH:
exit->x2 -= 10;
draw_stairs_up(room, SOUTH, 10, xpos, ypos);
break;
case SOUTH:
exit->x2 += 10;
draw_stairs_up(room, NORTH, 10, xpos, ypos);
break;
case EAST:
exit->y2 += 10;
draw_stairs_up(room, WEST, 10, xpos, ypos);
break;
case WEST:
exit->y2 -= 10;
draw_stairs_up(room, EAST, 10, xpos, ypos);
break;
default:
fatal_error("create_entrance(): unknown entrance direction");
}
compress_allocated_space(room);
}
else
fatal_error("create_entrance(): unknown entrance type");
}
/****************************************************************************
*
* process_exit_queue() - maintain a queue of all unfinished exits in the
* dungeon, when new exits are created, add them to
* the end of the queue, when ready to create a new
* location, grab an exit from the front of the queue,
* check if sufficient space can be allocated for the
* location, and if possible create the location
* (possibly with one or more exits), and removed the
* now-finished exit from the queue
*
****************************************************************************/
process_exit_queue()
{
int delta, width, xpos, ypos, flag;
EXITS *exit;
LOCAT *room;
/* loop until all exits are finished */
while (queuehead != NULL) {
exit = dequeue_exit();
/* if the exit is null, ignore it (it was probably nullified
by being merged with another exit to form a new location */
if (exit == NULL)
continue;
/* if the exit has no parent location, something is very
wrong */
if (exit->parent == NULL)
fatal_error("process_exit_queue(): found exit with no parent");
/* try to join two exits together to prevent cases where
locations on the map spread ever outward and never join
up, which would result it annoying, unrealistic maps */
if (join_exits(exit)) {
free_exit_node(exit);
continue;
}
/* allocate a new location for the exit */
room = new_location_node();
room->xmin = MIN(exit->x1, exit->x2);
room->xmax = MAX(exit->x1, exit->x2);
room->ymin = MIN(exit->y1, exit->y2);
room->ymax = MAX(exit->y1, exit->y2);
room->locationtype = exit->locationtype;
room->level = exit->level;
/* shift the entrance to the room so that it does not overlap
the previous room */
switch(exit->orientation) {
case NORTH: room->ymin += 1; room->ymax += 1; break;
case SOUTH: room->ymin -= 1; room->ymax -= 1; break;
case EAST: room->xmin += 1; room->xmax += 1; break;
case WEST: room->xmin -= 1; room->xmax -= 1; break;
}
/* determine the key position of the exit */
find_exit_position(exit, &width, &xpos, &ypos);
/* expand the allocated space of the location depending upon
what type of location it is */
if (exit->locationtype == JUNCTION)
delta = expand_allocated_space(room, exit->orientation, width);
else if (exit->locationtype == STAIRWAY)
delta = expand_allocated_space(room, exit->orientation, 20);
else if (exit->locationtype == PASSAGE)
delta = expand_allocated_space(room, exit->orientation,
((rand() % 8) * 5) + 25);
else
delta = expand_allocated_space(room, exit->orientation,
((rand() % 10) * 5) + 10);
/* if insufficient space is allocated, then scrap the location
and seal off the exit as if never existed */
if (delta < width) {
seal_exit(exit);
free_exit_node(exit);
continue;
}
flag = 0;
/* create the type of location requested by the exit */
switch (exit->locationtype) {
case ROOM:
case CHAMBER:
flag = draw_room(room, exit);
break;
case PASSAGE:
flag = draw_passage(room, exit, delta);
break;
case JUNCTION:
flag = draw_junction(room, exit, delta, width);
break;
case STAIRWAY:
flag = draw_stairway(room, exit, delta, width, xpos, ypos);
break;
default:
fatal_error("process_exit_queue(): unknown location type");
}
/* if an error occurred in location creation, scrap the exit */
if (flag) {
seal_exit(exit);
free_exit_node(exit);
continue;
}
/* otherwise draw the exit */
draw_exit(exit);
/* do not add locationf to queue until last so will not
interfer with checking of allocated space */
compress_allocated_space(room);
add_location_to_queue(room);
free_exit_node(exit);
}
}
/****************************************************************************
*
* join_exits() - try to join the given exit with a currently exiting exit
* so can have multiple paths to the same location, otherwise
* will end up with only one possible path connected two
* locations which looks strange and unrealistic
*
****************************************************************************/
int join_exits(exit)
EXITS *exit;
{
int mergeflag;
int sl, sr, nl, nr, et, eb, wt, wb;
int sflag, nflag, eflag, wflag;
int delta;
EXITS *otherexit;
LOCAT *newroom;
QUEUE *que;
/* do not join all exits that possibly can, only do some of them */
if (rand() % 2)
return 0;
/* do not mess with exits for stairways */
if (exit->locationtype == STAIRWAY)
return 0;
/* search for an exit to merge with */
mergeflag = 0;
nflag = sflag = eflag = wflag = 0;
que = queuehead;
while (que != NULL) {
if ((que->exit != NULL) && (que->exit->level == exit->level)
&& (que->exit->locationtype != STAIRWAY)) {
otherexit = que->exit;
if (otherexit->parent == NULL)
fatal_error("join_exits(): second exit has no parent\n");
if (exit->orientation == NORTH) {
sl = MIN(exit->x1, exit->x2);
sr = MAX(exit->x1, exit->x2);
sflag = 1;
if (otherexit->orientation == EAST) {
wt = MAX(otherexit->y1, otherexit->y2);
wb = MIN(otherexit->y1, otherexit->y2);
if ((otherexit->x1 < sl) && (exit->y1 < wb)
&& ((wt - exit->y1) <= 61)
&& ((sr - otherexit->x1) <= 61)
&& ((newroom = box_empty(wt, exit->y1+1,
otherexit->x1+1, sr, exit->level))
!= NULL)) {
wflag = 1;
mergeflag = SOUTH;
break;
}
}
else if (otherexit->orientation == SOUTH) {
nl = MIN(otherexit->x1, otherexit->x2);
nr = MAX(otherexit->x1, otherexit->x2);
if (((otherexit->y1 - 1) > (exit->y1 + 1))
&& ((otherexit->y1 - exit->y1) <= 62)
&& ((otherexit->y1 - exit->y1) > 11)
&& (abs(nl - sl) < 60)
&& ((newroom = box_empty(otherexit->y1 - 1,
exit->y1 + 1, MIN(nl, sl), MAX(nr, sr),
exit->level)) != NULL)) {
nflag = 1;
mergeflag = SOUTH;
break;
}
}
else if (otherexit->orientation == WEST) {
et = MAX(otherexit->y1, otherexit->y2);
eb = MIN(otherexit->y1, otherexit->y2);
if ((otherexit->x1 > sr) && (exit->y1 < eb)
&& ((et - exit->y1) <= 61)
&& ((otherexit->x1 - sl) <= 61)
&& ((newroom = box_empty(et, exit->y1 + 1, sl,
otherexit->x1 - 1, exit->level))
!= NULL)) {
eflag = 1;
mergeflag = SOUTH;
break;
}
}
}
else if (exit->orientation == SOUTH) {
nl = MIN(exit->x1, exit->x2);
nr = MAX(exit->x1, exit->x2);
nflag = 1;
if (otherexit->orientation == EAST) {
wt = MAX(otherexit->y1, otherexit->y2);
wb = MIN(otherexit->y1, otherexit->y2);
if ((otherexit->x1 < nl) && (exit->y1 > wt)
&& ((exit->y1 - wb) <= 61)
&& ((nr - otherexit->x1) <= 61)
&& ((newroom = box_empty(exit->y1-1, wb,
otherexit->x1+1, nr, exit->level))
!= NULL)) {
wflag = 1;
mergeflag = NORTH;
break;
}
}
else if (otherexit->orientation == NORTH) {
sl = MIN(otherexit->x1, otherexit->x2);
sr = MAX(otherexit->x1, otherexit->x2);
if (((otherexit->y1 + 1) < (exit->y1 - 1))
&& ((exit->y1 - otherexit->y1) <= 62)
&& ((exit->y1 - otherexit->y1) > 11)
&& (abs(nl - sl) <= 60)
&& ((newroom = box_empty(exit->y1 - 1,
otherexit->y1 + 1, MIN(nl,sl),
MAX(nr,sr), exit->level)) != NULL)) {
sflag = 1;
mergeflag = NORTH;
break;
}
}
else if (otherexit->orientation == WEST) {
et = MAX(otherexit->y1, otherexit->y2);
eb = MIN(otherexit->y1, otherexit->y2);
if ((otherexit->x1 > nr) && (exit->y1 > et)
&& ((exit->y1 - eb) <= 61)
&& ((otherexit->x1 - nl) <= 61)
&& ((newroom = box_empty(exit->y1 - 1, eb,
nl, otherexit->x1 - 1, exit->level))
!= NULL)) {
eflag = 1;
mergeflag = NORTH;
break;
}
}
}
else if (exit->orientation == EAST) {
wt = MAX(exit->y1, exit->y2);
wb = MIN(exit->y1, exit->y2);
wflag = 1;
if (otherexit->orientation == NORTH) {
sl = MIN(otherexit->x1, otherexit->x2);
sr = MAX(otherexit->x1, otherexit->x2);
if ((exit->x1 < sl) && (otherexit->y1 < wb)
&& ((sr - exit->x1) <= 61)
&& ((wt - otherexit->y1) <= 61)
&& ((newroom = box_empty(wt, otherexit->y1 + 1,
exit->x1 + 1, sr, exit->level))
!= NULL)) {
sflag = 1;
mergeflag = WEST;
break;
}
}
else if (otherexit->orientation == SOUTH) {
nl = MIN(otherexit->x1, otherexit->x2);
nr = MAX(otherexit->x1, otherexit->x2);
if ((exit->x1 < nl) && (wt < otherexit->y1)
&& ((nr - exit->x1) <= 61)
&& ((otherexit->y1 - wb) <= 61)
&& ((newroom = box_empty(otherexit->y1 - 1, wb,
exit->x1 + 1, nr, exit->level))
!= NULL)) {
nflag = 1;
mergeflag = WEST;
break;
}
}
else if (otherexit->orientation == WEST) {
et = MAX(otherexit->y1, otherexit->y2);
eb = MIN(otherexit->y1, otherexit->y2);
if (((exit->x1 + 1) < (otherexit->x1 - 1))
&& ((otherexit->x1 - exit->x1) <= 62)
&& ((otherexit->x1 - exit->x1) > 11)
&& (abs(et - wt) < 60)
&& ((newroom = box_empty(MAX(et, wt),
MIN(eb, wb), exit->x1 + 1,
otherexit->x1 - 1, exit->level))
!= NULL)) {
eflag = 1;
mergeflag = WEST;
break;
}
}
}
else if (exit->orientation == WEST) {
et = MAX(exit->y1, exit->y2);
eb = MIN(exit->y1, exit->y2);
eflag = 1;
if (otherexit->orientation == NORTH) {
sl = MIN(otherexit->x1, otherexit->x2);
sr = MAX(otherexit->x1, otherexit->x2);
if ((otherexit->y1 < eb) && (sr < exit->x1)
&& ((exit->x1 - sl) <= 61)
&& ((et - otherexit->y1) <= 61)
&& ((newroom = box_empty(et, otherexit->y1 + 1,
sl, exit->x1 - 1, exit->level))
!= NULL)) {
sflag = 1;
mergeflag = EAST;
break;
}
}
else if (otherexit->orientation == SOUTH) {
nl = MIN(otherexit->x1, otherexit->x2);
nr = MAX(otherexit->x1, otherexit->x2);
if ((nr < exit->x1) && (et < otherexit->y1)
&& ((exit->x1 - nl) <= 61)
&& ((otherexit->y1 - eb) <= 61)
&& ((newroom = box_empty(otherexit->y1 - 1,
eb, nl, exit->x1 - 1, exit->level))
!= NULL)) {
nflag = 1;
mergeflag = EAST;
break;
}
}
else if (otherexit->orientation == EAST) {
wt = MAX(otherexit->y1, otherexit->y2);
wb = MIN(otherexit->y1, otherexit->y2);
if (((otherexit->x1 + 1) < (exit->x1 - 1))
&& ((exit->x1 - otherexit->x1) <= 62)
&& ((exit->x1 - otherexit->x1) > 11)
&& (abs(et - wt) <= 61)
&& ((newroom = box_empty(MAX(et, wt),
MIN(eb, wb), otherexit->x1 + 1,
exit->x1 - 1, exit->level))
!= NULL)) {
wflag = 1;
mergeflag = EAST;
break;
}
}
}
}
que = que->queuenext;
}
if (!mergeflag)
return (0);
/* if reach this point, then have successfully found an exit to
merge with */
if (debugging)
printf("successful merging of two exits\n");
/* decide upon a type for the location, make small locations into
junctions to try and avoid having room numbers fall in the middle
of what appear to be hallways */
if (MIN(newroom->xmax - newroom->xmin, newroom->ymax - newroom->ymin) <=15)
newroom->locationtype = JUNCTION;
else
newroom->locationtype = ROOM;
newroom->level = exit->level;
add_location_to_queue(newroom);
/* draw the exits into this location, and try to add a few new ones */
if (nflag) {
if (mergeflag == NORTH)
draw_exit(exit);
else {
draw_exit(otherexit);
que->exit = NULL;
free_exit_node(otherexit);
}
}
else {
delta = expand_allocated_space(newroom, NORTH, (rand() % 3) * 5);
if (rand() % 2) {
nflag = 1;
create_exit(newroom, NORTH, &nr, &nl);
}
}
if (sflag) {
if (mergeflag == SOUTH)
draw_exit(exit);
else {
draw_exit(otherexit);
que->exit = NULL;
free_exit_node(otherexit);
}
}
else {
delta = expand_allocated_space(newroom, SOUTH, (rand() % 3) * 5);
if (rand() % 2) {
sflag = 1;
create_exit(newroom, SOUTH, &sr, &sl);
}
}
if (eflag) {
if (mergeflag == EAST)
draw_exit(exit);
else {
draw_exit(otherexit);
que->exit = NULL;
free_exit_node(otherexit);
}
}
else {
delta = expand_allocated_space(newroom, EAST, (rand() % 3) * 5);
if (rand() % 2) {
eflag = 1;
create_exit(newroom, EAST, &et, &eb);
}
}
if (wflag) {
if (mergeflag == WEST)
draw_exit(exit);
else {
draw_exit(otherexit);
que->exit = NULL;
free_exit_node(otherexit);
}
}
else {
delta = expand_allocated_space(newroom, WEST, (rand() % 3) * 5);
if (rand() % 2) {
wflag = 1;
create_exit(newroom, WEST, &wt, &wb);
}
}
/* draw the outline of the location */
if (nflag) {
add_line_to_location(newroom, newroom->xmin, newroom->ymax, nl,
newroom->ymax);
add_line_to_location(newroom, nr, newroom->ymax, newroom->xmax,
newroom->ymax);
}
else
add_line_to_location(newroom, newroom->xmin, newroom->ymax,
newroom->xmax, newroom->ymax);
if (sflag) {
add_line_to_location(newroom, newroom->xmin, newroom->ymin, sl,
newroom->ymin);
add_line_to_location(newroom, sr, newroom->ymin, newroom->xmax,
newroom->ymin);
}
else
add_line_to_location(newroom, newroom->xmin, newroom->ymin,
newroom->xmax, newroom->ymin);
if (eflag) {
add_line_to_location(newroom, newroom->xmax, newroom->ymax,
newroom->xmax, et);
add_line_to_location(newroom, newroom->xmax, eb, newroom->xmax,
newroom->ymin);
}
else
add_line_to_location(newroom, newroom->xmax, newroom->ymax,
newroom->xmax, newroom->ymin);
if (wflag) {
add_line_to_location(newroom, newroom->xmin, newroom->ymax,
newroom->xmin, wt);
add_line_to_location(newroom, newroom->xmin, wb, newroom->xmin,
newroom->ymin);
}
else
add_line_to_location(newroom, newroom->xmin, newroom->ymax,
newroom->xmin, newroom->ymin);
return 1;
}
/****************************************************************************
*
* box_empty() - given a box on a certain level of the dungeon, see if there
* are any locations that fall within that box, if not then
* allocate a location to fill that box and return the pointer
* to that location (return NULL if unable to create location)
*
****************************************************************************/
LOCAT *box_empty(top, bottom, left, right, level)
int top, bottom, left, right, level;
{
LOCAT *room, *newroom;
room = firstlocation;
while (room != NULL) {
if ((room->level == level)
&& ((room->xmax >= left) && (room->xmin <= right))
&& ((room->ymax >= bottom) && (room->ymin <= top)))
return (NULL);
room = room->next;
}
/* if reach here, then the location is vacant, allocate it and return
a pointer to it */
newroom = new_location_node();
newroom->xmax = right;
newroom->xmin = left;
newroom->ymax = top;
newroom->ymin = bottom;
return (newroom);
}
/****************************************************************************
*
* location_beyond_opening() - decide upon what type of a location should be
* found on the other side of an opening
*
****************************************************************************/
int location_beyond_opening()
{
int i, rtn;
i = rand() % 14;
if (i < 3)
rtn = CHAMBER;
else if (i < 5)
rtn = PASSAGE;
else if (i < 13)
rtn = JUNCTION;
else
rtn = STAIRWAY;
return (rtn);
}
/****************************************************************************
*
* location_beyond_doorway() - decide what kind of location should be found
* on the other side of a doorway
*
****************************************************************************/
int location_beyond_doorway()
{
int i, rtn;
i = rand() % 18;
if (i < 8)
rtn = ROOM;
else if (i < 10)
rtn = CHAMBER;
else if (i < 14)
rtn = PASSAGE;
else
rtn = JUNCTION;
return (rtn);
}
/****************************************************************************
*
* find_exit_postion() - given an exit, determine its key location and width,
* exits facing different directions with the same key
* position appear to revolve around that position
*
****************************************************************************/
find_exit_position(exit, width, xpos, ypos)
EXITS *exit;
int *width, *xpos, *ypos;
{
switch (exit->orientation) {
case NORTH:
*width = MAX(exit->x1, exit->x2) - MIN(exit->x1, exit->x2);
*xpos = MIN(exit->x1, exit->x2);
*ypos = exit->y1;
break;
case SOUTH:
*width = MAX(exit->x1, exit->x2) - MIN(exit->x1, exit->x2);
*xpos = MAX(exit->x1, exit->x2);
*ypos = exit->y1;
break;
case EAST:
*width = MAX(exit->y1, exit->y2) - MIN(exit->y1, exit->y2);
*xpos = exit->x1;
*ypos = MAX(exit->y1, exit->y2);
break;
case WEST:
*width = MAX(exit->y1, exit->y2) - MIN(exit->y1, exit->y2);
*xpos = exit->x1;
*ypos = MIN(exit->y1, exit->y2);
break;
}
}
/****************************************************************************
*
* draw_room() - expand the space for a room, create some exits for it, and
* draw all of the lines for that room/chamber
*
****************************************************************************/
int draw_room(room, exit)
LOCAT *room;
EXITS *exit;
{
int xne, yne, xse, yse, xsw, ysw, xnw, ynw;
int nflag, sflag, eflag, wflag;
int sl, sr, nl, nr, et, eb, wt, wb;
int delta2, delta3;
if (debugging)
printf("drawing a room\n");
/* expand space to either side of exit so that it does not look like
a passage */
delta2 = expand_allocated_space(room, left_direction(exit->orientation),
(rand() % 5) * 5);
delta3 = expand_allocated_space(room, right_direction(exit->orientation),
(rand() % 5) * 5);
/* get the four corners of the room */
xne = xse = room->xmax;
xnw = xsw = room->xmin;
yne = ynw = room->ymax;
yse = ysw = room->ymin;
/* make certain that the room measures at least ten feet on a side */
if (MIN((room->xmax - room->xmin), (room->ymax - room->ymin)) < 10)
return 1;
nflag = sflag = eflag = wflag = 0;
/* if the entrance to the room faces a certain direction, get either
side of it, otherwise randomly place an exit in that wall */
if (exit->orientation == NORTH) {
sl = MIN(exit->x1, exit->x2);
sr = MAX(exit->x1, exit->x2);
sflag = 1;
}
else {
if (rand() % 2) {
sflag = 1;
create_exit(room, SOUTH, &sr, &sl);
}
}
if (exit->orientation == SOUTH) {
nl = MIN(exit->x1, exit->x2);
nr = MAX(exit->x1, exit->x2);
nflag = 1;
}
else {
if (rand() % 2) {
nflag = 1;
create_exit(room, NORTH, &nr, &nl);
}
}
if (exit->orientation == EAST) {
wt = MAX(exit->y1, exit->y2);
wb = MIN(exit->y1, exit->y2);
wflag = 1;
}
else {
if (rand() % 2) {
wflag = 1;
create_exit(room, WEST, &wt, &wb);
}
}
if (exit->orientation == WEST) {
et = MAX(exit->y1, exit->y2);
eb = MIN(exit->y1, exit->y2);
eflag = 1;
}
else {
if (rand() % 2) {
eflag = 1;
create_exit(room, EAST, &et, &eb);
}
}
/* draw the four walls of the room, leaving openings for exits */
if (nflag) {
add_line_to_location(room, xnw, ynw, nl, yne);
add_line_to_location(room, nr, ynw, xne, yne);
}
else
add_line_to_location(room, xnw, ynw, xne, yne);
if (sflag) {
add_line_to_location(room, xsw, ysw, sl, yse);
add_line_to_location(room, sr, ysw, xse, yse);
}
else
add_line_to_location(room, xsw, ysw, xse, yse);
if (eflag) {
add_line_to_location(room, xne, yne, xne, et);
add_line_to_location(room, xse, eb, xse, yse);
}
else
add_line_to_location(room, xne, yne, xse, yse);
if (wflag) {
add_line_to_location(room, xnw, ynw, xnw, wt);
add_line_to_location(room, xsw, wb, xsw, ysw);
}
else
add_line_to_location(room, xnw, ynw, xsw, ysw);
return 0;
}
/****************************************************************************
*
* create_exit() - create an exit facing a certain direction
*
****************************************************************************/
create_exit(room, orient, high, low)
LOCAT *room;
int orient, *high, *low;
{
int i, length, over, exitwidth;
EXITS *newexit;
/* get length of wall exit is in */
if ((orient == NORTH) || (orient == SOUTH))
length = room->xmax - room->xmin;
else
length = room->ymax - room->ymin;
i = rand() % 10;
/* decide upon a size for the exit */
if ((i < 2) && (length >= 15))
exitwidth = 15;
else if ((i < 8) && (length >= 10))
exitwidth = 10;
else
exitwidth = 5;
/* decide upon where to place the exit in the wall */
if ((length - exitwidth) > 1)
over = rand() % (length - exitwidth);
else
over = 0;
/* create the exit and add it to the queue */
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->parent = room;
newexit->level = room->level;
newexit->orientation = orient;
/* set the location of the exit */
if ((orient == NORTH) || (orient == SOUTH)) {
newexit->x1 = *high = room->xmax - over;
newexit->x2 = *low = room->xmax - over - exitwidth;
if (orient == NORTH)
newexit->y1 = newexit->y2 = room->ymax;
else
newexit->y1 = newexit->y2 = room->ymin;
}
else {
newexit->y1 = *high = room->ymax - over;
newexit->y2 = *low = room->ymax - over - exitwidth;
if (orient == EAST)
newexit->x1 = newexit->x2 = room->xmax;
else
newexit->x1 = newexit->x2 = room->xmin;
}
/* determine what type of location lies on the other side of the
exit */
if (room->locationtype == ROOM) {
newexit->exittype = DOORWAY;
newexit->locationtype = location_beyond_doorway();
}
else {
newexit->exittype = OPENING;
newexit->locationtype = location_beyond_opening();
}
}
/****************************************************************************
*
* draw_passage() - draw a passage from an exit: basically extend a hallway
* in the direction the exit is facing and place a new exit
* in the far end of the hallway
*
****************************************************************************/
int draw_passage(room, exit, delta)
LOCAT *room;
EXITS *exit;
int delta;
{
EXITS *newexit;
if (debugging)
printf("drawing a passage\n");
/* create the exit in the far end of the hallway */
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->orientation = exit->orientation;
newexit->parent = room;
newexit->level = room->level;
newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
/* set the type of location beyond the exit */
if (newexit->exittype == DOORWAY)
newexit->locationtype = location_beyond_doorway();
else
newexit->locationtype = location_beyond_opening();
/* draw the walls of the passage and set the position of new exit */
switch (exit->orientation) {
case NORTH:
case SOUTH:
if (exit->orientation == NORTH) {
newexit->y1 = exit->y1 + delta + 1;
newexit->y2 = exit->y2 + delta + 1;
}
else {
newexit->y1 = exit->y1 - delta - 1;
newexit->y2 = exit->y2 - delta - 1;
}
newexit->x1 = exit->x1;
newexit->x2 = exit->x2;
add_line_to_location(room, room->xmin, room->ymin,
room->xmin, room->ymax);
add_line_to_location(room, room->xmax, room->ymin,
room->xmax, room->ymax);
break;
case EAST:
case WEST:
if (exit->orientation == EAST) {
newexit->x1 = exit->x1 + delta + 1;
newexit->x2 = exit->x2 + delta + 1;
}
else {
newexit->x1 = exit->x1 - delta - 1;
newexit->x2 = exit->x2 - delta - 1;
}
newexit->y1 = exit->y1;
newexit->y2 = exit->y2;
add_line_to_location(room, room->xmin, room->ymin,
room->xmax, room->ymin);
add_line_to_location(room, room->xmin, room->ymax,
room->xmax, room->ymax);
break;
}
return 0;
}
/****************************************************************************
*
* draw_junction() - draw a passage junction, which is basically a location
* with an exit that takes the entire length of a wall
* but each wall is only the width of a passage
*
****************************************************************************/
int draw_junction(room, exit, delta, width)
LOCAT *room;
EXITS *exit;
int delta, width;
{
int xnw, ynw, xne, yne, xse, yse, xsw, ysw, count;
EXITS *newexit;
if (debugging)
printf("drawing a junction\n");
count = 0;
/* make sure a square area was allocated for the junction */
if (delta < width)
seal_exit(exit);
else {
switch (exit->orientation) {
case NORTH:
xnw = xsw = MIN(exit->x1, exit->x2);
xne = xse = MAX(exit->x1, exit->x2);
ynw = yne = exit->y1 + 1 + width;
ysw = yse = exit->y1 + 1;
break;
case SOUTH:
xnw = xsw = MIN(exit->x1, exit->x2);
xne = xse = MAX(exit->x1, exit->x2);
ynw = yne = exit->y1 - 1;
ysw = yse = exit->y1 - 1 - width;
break;
case EAST:
xne = xse = exit->x1 + 1 + width;
xnw = xsw = exit->x1 + 1;
ynw = yne = MAX(exit->y1, exit->y2);
ysw = yse = MIN(exit->y1, exit->y2);
break;
case WEST:
xne = xse = exit->x1 - 1;
xnw = xsw = exit->x1 - 1 - width;
ynw = yne = MAX(exit->y1, exit->y2);
ysw = yse = MIN(exit->y1, exit->y2);
break;
}
/* add zero-length lines at each corner to assure that
the necessary allocated space is not compressed */
add_line_to_location(room, xnw, ynw, xnw, ynw);
add_line_to_location(room, xne, yne, xne, yne);
add_line_to_location(room, xse, yse, xse, yse);
add_line_to_location(room, xsw, ysw, xsw, ysw);
/* if there is no exit in the north wall (facing south), then
plane an exit in the north wall with .666 probability */
if (exit->orientation != SOUTH) {
if (rand() % 3) {
++count;
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->orientation = NORTH;
newexit->parent = room;
newexit->level = room->level;
newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
newexit->x1 = xnw;
newexit->y1 = ynw;
newexit->x2 = xne;
newexit->y2 = yne;
if (newexit->exittype == DOORWAY)
newexit->locationtype = location_beyond_doorway();
else
newexit->locationtype = location_beyond_opening();
}
else
add_line_to_location(room, xnw, ynw, xne, yne);
}
if (exit->orientation != NORTH) {
if (rand() % 3) {
++count;
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->orientation = SOUTH;
newexit->parent = room;
newexit->level = room->level;
newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
newexit->x1 = xse;
newexit->y1 = yse;
newexit->x2 = xsw;
newexit->y2 = ysw;
if (newexit->exittype == DOORWAY)
newexit->locationtype = location_beyond_doorway();
else
newexit->locationtype = location_beyond_opening();
}
else
add_line_to_location(room, xsw, ysw, xse, yse);
}
if (exit->orientation != WEST) {
if (rand() % 3) {
++count;
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->orientation = EAST;
newexit->parent = room;
newexit->level = room->level;
newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
newexit->x1 = xne;
newexit->y1 = yne;
newexit->x2 = xse;
newexit->y2 = yse;
if (newexit->exittype == DOORWAY)
newexit->locationtype = location_beyond_doorway();
else
newexit->locationtype = location_beyond_opening();
}
else
add_line_to_location(room, xne, yne, xse, yse);
}
if (exit->orientation != EAST) {
if (rand() % 3) {
++count;
newexit = new_exit_node();
add_exit_to_queue(newexit);
newexit->orientation = WEST;
newexit->parent = room;
newexit->level = room->level;
newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
newexit->x1 = xsw;
newexit->y1 = ysw;
newexit->x2 = xnw;
newexit->y2 = ynw;
if (newexit->exittype == DOORWAY)
newexit->locationtype = location_beyond_doorway();
else
newexit->locationtype = location_beyond_opening();
}
else
add_line_to_location(room, xnw, ynw, xsw, ysw);
}
}
/* return true if no exits were added to the junction, so that
the junction will be junked */
return (!count);
}
/****************************************************************************
*
* draw_stairway() - draw a stairway randomly going up or down, but abort if
* go above the first level or below the maximum level
*
****************************************************************************/
int draw_stairway(room, exit, delta, width, xpos, ypos)
LOCAT *room;
EXITS *exit;
int delta, width, xpos, ypos;
{
LOCAT *newroom;
EXITS *newexit;
if (debugging)
printf("drawing a stairway\n");
/* space must be 20 feet long for the stairway */
if (delta < 20)
return 1;
/* set the location of the stairway on the next level */
newroom = new_location_node();
newroom->xmin = MIN(exit->x1, exit->x2);
newroom->xmax = MAX(exit->x1, exit->x2);
newroom->ymin = MIN(exit->y1, exit->y2);
newroom->ymax = MAX(exit->y1, exit->y2);
newroom->locationtype = STAIRWAY;
/* give an exit for the stairway on the new level */
newexit = new_exit_node();
newexit->x1 = exit->x1;
newexit->y1 = exit->y1;
newexit->x2 = exit->x2;
newexit->y2 = exit->y2;
newexit->orientation = exit->orientation;
newexit->locationtype = location_beyond_opening();
newexit->exittype = OPENING;
newexit->parent = newroom;
/* determine the postion of the exit at the top/bottom of the stairs */
switch (exit->orientation) {
case NORTH: newexit->y1 += 20; newexit->y2 += 20; break;
case SOUTH: newexit->y1 -= 20; newexit->y2 -= 20; break;
case EAST: newexit->x1 += 20; newexit->x2 += 20; break;
case WEST: newexit->x1 -= 20; newexit->x2 -= 20; break;
}
/* stairs go up or down with random probability */
if (rand() % 2) {
if (room->level <= startlevel)
return 1;
/* set the stairway to go up (decreasing level number) */
room->locationdata = UP;
newroom->locationdata = DOWN;
newroom->level = room->level - 1;
delta = expand_allocated_space(newroom, exit->orientation, 20);
if (delta < 20)
return 1;
draw_stairs_up(room, exit->orientation, width, xpos, ypos);
switch (exit->orientation) {
case NORTH:
draw_stairs_down(newroom, SOUTH, width, xpos+width, ypos+20);
break;
case SOUTH:
draw_stairs_down(newroom, NORTH, width, xpos-width, ypos-20);
break;
case EAST:
draw_stairs_down(newroom, WEST, width, xpos+20, ypos-width);
break;
case WEST:
draw_stairs_down(newroom, EAST, width, xpos-20, ypos+width);
break;
}
}
else {
if (room->level >= maxlevel)
return 1;
/* set the stairway going down (increasing level number) */
room->locationdata = DOWN;
newroom->locationdata = UP;
newroom->level = room->level + 1;
delta = expand_allocated_space(newroom, exit->orientation, 20);
if (delta < 20)
return 1;
draw_stairs_down(room, exit->orientation, width, xpos, ypos);
switch (exit->orientation) {
case NORTH:
draw_stairs_up(newroom, SOUTH, width, xpos+width, ypos+20);
break;
case SOUTH:
draw_stairs_up(newroom, NORTH, width, xpos-width, ypos-20);
break;
case EAST:
draw_stairs_up(newroom, WEST, width, xpos+20, ypos-width);
break;
case WEST:
draw_stairs_up(newroom, EAST, width, xpos-20, ypos+width);
break;
}
}
newexit->level = newroom->level;
add_location_to_queue(newroom);
add_exit_to_queue(newexit);
/* always return with no error condition */
return 0;
}
/****************************************************************************
*
* draw_stairs_down() - draw the lines for a stairway going down
*
****************************************************************************/
draw_stairs_down(room, orient, width, xpos, ypos)
LOCAT *room;
int orient, width, xpos, ypos;
{
int i;
for (i = 0; i < 11; ++i) {
switch (width) {
case 5:
add_line_to_location(room,
xpos + stairwaydownlines5[orient-NORTH][i][0],
ypos + stairwaydownlines5[orient-NORTH][i][1],
xpos + stairwaydownlines5[orient-NORTH][i][2],
ypos + stairwaydownlines5[orient-NORTH][i][3]);
break;
case 10:
add_line_to_location(room,
xpos + stairwaydownlines10[orient-NORTH][i][0],
ypos + stairwaydownlines10[orient-NORTH][i][1],
xpos + stairwaydownlines10[orient-NORTH][i][2],
ypos + stairwaydownlines10[orient-NORTH][i][3]);
break;
case 15:
add_line_to_location(room,
xpos + stairwaydownlines15[orient-NORTH][i][0],
ypos + stairwaydownlines15[orient-NORTH][i][1],
xpos + stairwaydownlines15[orient-NORTH][i][2],
ypos + stairwaydownlines15[orient-NORTH][i][3]);
break;
default:
printf("width: %d\n", width);
fatal_error("draw_stairs_down(): bad width of stairway");
}
}
}
/****************************************************************************
*
* draw_stairs_up() - draw the lines for a stairway going up
*
****************************************************************************/
draw_stairs_up(room, orient, width, xpos, ypos)
LOCAT *room;
int orient, width, xpos, ypos;
{
int i;
for (i = 0; i < 11; ++i) {
switch (width) {
case 5:
add_line_to_location(room,
xpos + stairwayuplines5[orient-NORTH][i][0],
ypos + stairwayuplines5[orient-NORTH][i][1],
xpos + stairwayuplines5[orient-NORTH][i][2],
ypos + stairwayuplines5[orient-NORTH][i][3]);
break;
case 10:
add_line_to_location(room,
xpos + stairwayuplines10[orient-NORTH][i][0],
ypos + stairwayuplines10[orient-NORTH][i][1],
xpos + stairwayuplines10[orient-NORTH][i][2],
ypos + stairwayuplines10[orient-NORTH][i][3]);
break;
case 15:
add_line_to_location(room,
xpos + stairwayuplines15[orient-NORTH][i][0],
ypos + stairwayuplines15[orient-NORTH][i][1],
xpos + stairwayuplines15[orient-NORTH][i][2],
ypos + stairwayuplines15[orient-NORTH][i][3]);
break;
default:
printf("width: %d\n", width);
fatal_error("draw_stairs_up(): bad width of stairway");
}
}
}
/****************************************************************************
*
* draw_exit() - given an exit, draw the actual lines that form it, the lines
* which form an exit are placed with the parent of the exit
*
****************************************************************************/
draw_exit(exit)
EXITS *exit;
{
int width, xpos, ypos;
find_exit_position(exit, &width, &xpos, &ypos);
if (exit->parent == NULL)
fatal_error("draw_exit(): exit has no parent pointer");
if (exit->exittype == DOORWAY)
draw_doorway(exit->parent, exit->orientation, width, xpos, ypos);
else if (exit->exittype == OPENING)
draw_opening(exit->parent, exit->orientation, width, xpos, ypos);
else
fatal_error("process_exit_queue(): unknown exit type");
}
/****************************************************************************
*
* draw_doorway() - draw the lines for a doorway
*
****************************************************************************/
draw_doorway(room, orient, width, xpos, ypos)
LOCAT *room;
int orient, width, xpos, ypos;
{
int i;
if (debugging)
printf("drawing a doorway\n");
switch (width) {
case 5:
for (i = 0; i < 8; ++i)
add_line_to_location(room,
xpos + doorwaylines5[orient-NORTH][i][0],
ypos + doorwaylines5[orient-NORTH][i][1],
xpos + doorwaylines5[orient-NORTH][i][2],
ypos + doorwaylines5[orient-NORTH][i][3]);
break;
case 10:
for (i = 0; i < 8; ++i)
add_line_to_location(room,
xpos + doorwaylines10[orient-NORTH][i][0],
ypos + doorwaylines10[orient-NORTH][i][1],
xpos + doorwaylines10[orient-NORTH][i][2],
ypos + doorwaylines10[orient-NORTH][i][3]);
break;
case 15:
for (i = 0; i < 9; ++i)
add_line_to_location(room,
xpos + doorwaylines15[orient-NORTH][i][0],
ypos + doorwaylines15[orient-NORTH][i][1],
xpos + doorwaylines15[orient-NORTH][i][2],
ypos + doorwaylines15[orient-NORTH][i][3]);
break;
default:
printf("width given as %d\n", width);
fatal_error("draw_doorway(): bad width of doorway");
}
}
/****************************************************************************
*
* draw_opening() - draw the lines for an opening in the wall
*
****************************************************************************/
draw_opening(room, orient, width, xpos, ypos)
LOCAT *room;
int orient, width, xpos, ypos;
{
if (debugging)
printf("drawing an opening\n");
switch (orient) {
case NORTH:
add_line_to_location(room, xpos, ypos, xpos, ypos+1);
add_line_to_location(room, xpos+width, ypos, xpos+width,
ypos+1);
break;
case SOUTH:
add_line_to_location(room, xpos, ypos, xpos, ypos-1);
add_line_to_location(room, xpos-width, ypos, xpos-width,
ypos-1);
break;
case EAST:
add_line_to_location(room, xpos, ypos, xpos+1, ypos);
add_line_to_location(room, xpos, ypos-width, xpos+1,
ypos-width);
break;
case WEST:
add_line_to_location(room, xpos, ypos, xpos-1, ypos);
add_line_to_location(room, xpos, ypos+width, xpos-1,
ypos+width);
break;
}
}
/****************************************************************************
*
* seal_exit() - seal off an exit by drawing a line across the space in the
* wall where it should have gone
*
****************************************************************************/
seal_exit(exit)
EXITS *exit;
{
if (debugging)
printf("sealing an exit\n");
add_line_to_location(exit->parent, exit->x1, exit->y1, exit->x2, exit->y2);
}
/****************************************************************************
*
* add_line_to_location() - given the endpoints of a line, place the line in
* the list of lines for the room
*
****************************************************************************/
add_line_to_location(room, x1, y1, x2, y2)
LOCAT *room;
int x1, y1, x2, y2;
{
LINES *line;
line = new_line_node();
line->next = room->firstline;
room->firstline = line;
line->x1 = x1;
line->y1 = y1;
line->x2 = x2;
line->y2 = y2;
}
/****************************************************************************
*
* compress_allocated_space() - find the minimum amount of space occupied by
* a location, to make sure the location is not
* accidentally allocated more than it needs
*
****************************************************************************/
compress_allocated_space(room)
LOCAT *room;
{
int minx = mapwidth, miny = mapheight, maxx = 0, maxy = 0;
LINES *line;
line = room->firstline;
/* do nothing if here are no lines */
if (line == NULL)
return;
while (line != NULL) {
minx = MIN(minx, line->x1);
minx = MIN(minx, line->x2);
maxx = MAX(maxx, line->x1);
maxx = MAX(maxx, line->x2);
miny = MIN(miny, line->y1);
miny = MIN(miny, line->y2);
maxy = MAX(maxy, line->y1);
maxy = MAX(maxy, line->y2);
line = line->next;
}
room->xmin = minx;
room->xmax = maxx;
room->ymin = miny;
room->ymax = maxy;
}
/****************************************************************************
*
* expand_allocated_space() - expand the space allocated to a location in
* a given direction for a given distance,
* and return the amount actually expanded
*
****************************************************************************/
int expand_allocated_space(location, direction, maxdelta)
LOCAT *location;
int direction, maxdelta;
{
int delta, lvl;
LOCAT *room;
/* just return zero if amount to expand is less than one */
if (maxdelta < 1)
return (0);
lvl = location->level;
switch (direction) {
case NORTH:
/* try to expand the allocated space as far as
possible to the north */
delta = mapheight;
room = firstlocation;
while (room != NULL) {
if ((room->level == lvl)
&& (room->xmax >= location->xmin)
&& (room->xmin <= location->xmax)
&& (room->ymax >= location->ymin))
delta = MIN(delta, room->ymin);
room = room->next;
}
/* get the amount of change and decrement it, so
allocated space does not overlap another location */
delta = delta - location->ymax - 1;
/* make sure do not expand it more than requested */
if (delta > maxdelta)
delta = maxdelta;
/* if actually allocated some space, increase the
amount of space allocated */
if (delta > 0)
location->ymax += delta;
break;
case SOUTH:
delta = 0;
room = firstlocation;
while (room != NULL) {
if ((room->level == lvl)
&& (room->xmax >= location->xmin)
&& (room->xmin <= location->xmax)
&& (room->ymin <= location->ymax))
delta = MAX(delta, room->ymax);
room = room->next;
}
delta = location->ymin - delta - 1;
if (delta > maxdelta)
delta = maxdelta;
if (delta > 0)
location->ymin -= delta;
break;
case EAST:
delta = mapwidth;
room = firstlocation;
while (room != NULL) {
if ((room->level == lvl)
&& (room->ymax >= location->ymin)
&& (room->ymin <= location->ymax)
&& (room->xmax >= location->xmin))
delta = MIN(delta, room->xmin);
room = room->next;
}
delta = delta - location->xmax - 1;
if (delta > maxdelta)
delta = maxdelta;
if (delta > 0)
location->xmax += delta;
break;
case WEST:
delta = 0;
room = firstlocation;
while (room != NULL) {
if ((room->level == lvl)
&& (room->ymax >= location->ymin)
&& (room->ymin <= location->ymax)
&& (room->xmin <= location->xmax))
delta = MAX(delta, room->xmax);
room = room->next;
}
delta = location->xmin - delta - 1;
if (delta > maxdelta)
delta = maxdelta;
if (delta > 0)
location->xmin -= delta;
break;
default:
fatal_error("expand_allocated_space(): unknown direction");
}
/* return the amount that the allocated space was expanded */
return (delta);
}
/****************************************************************************
*
* opposite_direction() - return the direction opposite to that given
*
****************************************************************************/
int opposite_direction(dir)
int dir;
{
int i;
switch (dir) {
case NORTH: i = SOUTH; break;
case SOUTH: i = NORTH; break;
case EAST: i = WEST; break;
case WEST: i = EAST; break;
case UP: i = DOWN; break;
case DOWN: i = UP; break;
default: fatal_error("opposite_direction(): unknown direction");
}
return (i);
}
/****************************************************************************
*
* left_direction() - return the direction to the left of that given
*
****************************************************************************/
int left_direction(dir)
int dir;
{
int i;
switch (dir) {
case NORTH: i = WEST; break;
case SOUTH: i = EAST; break;
case EAST: i = NORTH; break;
case WEST: i = SOUTH; break;
default: fatal_error("left_direction(): unknown direction");
}
return (i);
}
/****************************************************************************
*
* right_direction() - return the direction to the right of that given
*
****************************************************************************/
int right_direction(dir)
int dir;
{
int i;
switch (dir) {
case NORTH: i = EAST; break;
case SOUTH: i = WEST; break;
case EAST: i = SOUTH; break;
case WEST: i = NORTH; break;
default: fatal_error("right_direction(): unknown direction");
}
return (i);
}
/****************************************************************************
*
* print_postscript_file() - print out the dungeon in PostScript format
*
****************************************************************************/
print_postscript_file()
{
char fname[100];
int lvl;
FILE *fopen(), *outfile;
LOCAT *room;
LINES *line;
/* print out a separate file for each level, since they can get big */
for (lvl = startlevel; lvl <= maxlevel; ++lvl) {
/* get the name of the file */
sprintf(fname, "%s%d.ps", filename, lvl);
printf("Drawing dungeon level %d to file \"%s\".\n", lvl, fname);
if ((outfile = fopen(fname, "w")) == NULL)
fatal_error("unable to open file \"%s\" for output", fname);
/* print out the postscript header */
fprintf(outfile, "%%!PS-Adobe-1.0\n\n");
fprintf(outfile, "/Times-Roman findfont 12 scalefont\nsetfont\n\n");
fprintf(outfile, "newpath\n 50 750 moveto\n(Level %d) show\n\n", lvl);
fprintf(outfile, "/Times-Roman findfont 6 scalefont\nsetfont\n\n");
/* print out a boarder for the level if not built into the
level due to having a doorway entrance to the level */
if ((lvl != startlevel) || (entrancetype != DOORWAY)) {
fprintf(outfile, "newpath\n 50 30 moveto\n 50 %4d lineto\n",
YSIZE + 30);
fprintf(outfile, " 50 30 moveto\n %4d 30 lineto\n",
XSIZE + 50);
fprintf(outfile, " %4d 30 moveto\n %4d %4d lineto\n",
XSIZE + 50, XSIZE + 50, YSIZE + 30);
fprintf(outfile, " 50 %4d moveto\n %4d %4d lineto\nstroke\n\n",
YSIZE + 30, XSIZE + 50, YSIZE + 30);
}
/* loop through all locations and print out the ones on the
current level */
room = firstlocation;
while (room != NULL) {
if (room->level == lvl) {
line = room->firstline;
/* print the room number if it is a numbered location
and the user has requested numbering be turned on */
if (numberoutput && (room->number != 0))
fprintf(outfile, "newpath\n %d %d moveto\n(%d) show\n\n",
scale_x(room->xmin + 2) + 50,
scale_y(room->ymin + 2) + 30, room->number);
/* print out all lines associated with this location
(provided the lines have a non-zero length */
fprintf(outfile, "newpath\n");
while (line != NULL) {
if ((line->x1 != line->x2) || (line->y1 != line->y2))
fprintf(outfile, " %d %d moveto\n %d %d lineto\n",
scale_x(line->x1) + 50, scale_y(line->y1) +30,
scale_x(line->x2) + 50, scale_y(line->y2) +30);
line = line->next;
}
fprintf(outfile, "stroke\n\n");
}
room = room->next;
}
/* close the file for the current level */
fprintf(outfile, "showpage\n");
fclose(outfile);
}
}
/****************************************************************************
****************************************************************************/
print_latex_file()
{
char fname[100];
int i, dx, dy, length, lvl;
FILE *fopen(), *outfile;
LOCAT *room;
LINES *line;
/* print out a separate file for each level, since they can get big */
for (lvl = startlevel; lvl <= maxlevel; ++lvl) {
/* get the name of the file */
sprintf(fname, "%s%d.tex", filename, lvl);
printf("Drawing dungeon level %d to file \"%s\".\n", lvl, fname);
if ((outfile = fopen(fname, "w")) == NULL)
fatal_error("unable to open file \"%s\" for output", fname);
/* print out the latex header */
for (i = 0; i < LATEXHEADERLEN; ++i)
fprintf(outfile, "%s\n", latexheader[i]);
fprintf(outfile, "\n\\put(10,720){\\shortstack{Level %d}}\n\n", lvl);
/* print out a boarder for the level if not built into the
level due to having a doorway entrance to the level */
if ((lvl != startlevel) || (entrancetype != DOORWAY)) {
fprintf(outfile, "\\put(0,0){\\line(0,1){%d}}\n",
YSIZE);
fprintf(outfile, "\\put(0,0){\\line(1,0){%d}}\n",
XSIZE);
fprintf(outfile, "\\put(0,%d){\\line(1,0){%d}}\n",
YSIZE, XSIZE);
fprintf(outfile, "\\put(%d,0){\\line(0,1){%d}}\n",
XSIZE, YSIZE);
}
/* loop through all locations and print out the ones on the
current level */
room = firstlocation;
while (room != NULL) {
if (room->level == lvl) {
/* print the room number if it is a numbered location
and the user has requested numbering be turned on */
if (numberoutput && (room->number != 0))
fprintf(outfile,"\\put(%d,%d){\\shortstack{\\small{%d}}}\n",
scale_x(room->xmin + 2), scale_x(room->ymin + 2),
room->number);
/* print out all lines associated with this location
(provided the lines have a non-zero length */
line = room->firstline;
while (line != NULL) {
dx = 0;
dy = 0;
if (line->x1 != line->x2) {
dx = ((line->x1 < line->x2) ? (1) : (-1));
length = abs(scale_x(line->x1) - scale_x(line->x2));
}
else if (line->y1 != line->y2) {
dy = ((line->y1 < line->y2) ? (1) : (-1));
length = abs(scale_y(line->y1) - scale_y(line->y2));
}
else {
line = line->next;
continue;
}
fprintf(outfile, "\\put(%d,%d){\\line(%d,%d){%d}}\n",
scale_x(line->x1), scale_y(line->y1),
dx, dy, length);
line = line->next;
}
}
room = room->next;
}
/* close the file for the current level */
fprintf(outfile, "\n\\end{picture}\n\\end{document}\n");
fclose(outfile);
}
}
/****************************************************************************
*
* scale_x() - scale the position of a point to the map's scale
*
****************************************************************************/
int scale_x(foo)
int foo;
{
foo = ((float)XSIZE / (float)mapwidth) * (float)foo;
return (foo);
}
/****************************************************************************
*
* scale_y() - scale a point's position so it fits the size of the map
*
****************************************************************************/
int scale_y(foo)
int foo;
{
foo = ((float)YSIZE / (float)mapheight) * (float)foo;
return (foo);
}
/****************************************************************************
*
* new_exit_node() - return a pointer to a new (or reused) exit node
*
****************************************************************************/
EXITS *new_exit_node()
{
EXITS *ext;
if (exitfreestack != NULL) {
ext = exitfreestack;
exitfreestack = exitfreestack->next;
}
else if ((ext = (EXITS*)malloc(sizeof(EXITS))) == NULL)
fatal_error("new_Exit_node(): malloc memory failed");
ext->orientation = 0;
ext->x1 = 0;
ext->y1 = 0;
ext->x2 = 0;
ext->y2 = 0;
ext->exittype = 0;
ext->locationtype = ROOM;
ext->parent = NULL;
ext->next = NULL;
ext->level = 0;
return (ext);
}
/****************************************************************************
*
* free_exit_node() - free up the memory used by an exit node
*
****************************************************************************/
free_exit_node(ext)
EXITS *ext;
{
ext->parent = NULL;
ext->next = exitfreestack;
exitfreestack = ext;
}
/****************************************************************************
*
* new_line_node() - return a pointer to a new line node
*
****************************************************************************/
LINES *new_line_node()
{
LINES *lin;
if ((lin = (LINES*)malloc(sizeof(LINES))) == NULL)
fatal_error("new_line_node(): malloc memory failed");
lin->x1 = 0;
lin->y1 = 0;
lin->x2 = 0;
lin->y2 = 0;
lin->next = NULL;
return (lin);
}
/****************************************************************************
*
* new_location_node() - return a pointer to a new location node
*
****************************************************************************/
LOCAT *new_location_node()
{
LOCAT *loc;
if ((loc = (LOCAT*)malloc(sizeof(LOCAT))) == NULL)
fatal_error("new_location_node(): malloc memory failed");
loc->xmin = 0;
loc->xmax = 0;
loc->ymin = 0;
loc->ymax = 0;
loc->locationtype = ROOM;
loc->locationdata = -666;
loc->level = 0;
loc->number = 0;
loc->firstline = NULL;
loc->next = NULL;
loc->previous = NULL;
return (loc);
}
/****************************************************************************
*
* new_queue_node() - return a pointer to a new (or reused) exit queue node
*
****************************************************************************/
QUEUE *new_queue_node()
{
QUEUE *que;
if (queuefreestack != NULL) {
que = queuefreestack;
queuefreestack = queuefreestack->queuenext;
}
else if ((que = (QUEUE*)malloc(sizeof(QUEUE))) == NULL)
fatal_error("new_queue_node(): malloc memory failed");
que->exit = NULL;
que->queuenext = NULL;
return (que);
}
/****************************************************************************
*
* free_queue_node() - free up the memory used by an exit queue node
*
****************************************************************************/
free_queue_node(que)
QUEUE *que;
{
que->queuenext = queuefreestack;
queuefreestack = que;
que->exit = NULL;
}
/****************************************************************************
*
* add_exit_to_queue() - and an exit to the queue of exit nodes
*
****************************************************************************/
add_exit_to_queue(exit)
EXITS *exit;
{
QUEUE *que;
++gc;
if (debugging)
printf("adding exit to queue, newsize: %d\n", gc);
else {
printf("%4d", gc);
++gcnum;
if (gcnum > 15) {
gcnum = 0;
printf("\n");
}
}
que = new_queue_node();
que->exit = exit;
if (queuehead == NULL)
queuehead = queuetail = que;
else {
/* decude whether to place new exit at front or end of queue */
if (EXITSTACK) { /* do as stack */
que->queuenext = queuehead;
queuehead = que;
}
else { /* do as queue */
queuetail->queuenext = que;
queuetail = que;
}
}
}
/****************************************************************************
*
* dequeue_exit() - remove an exit from the queue of exit nodes, note that
* the pointer which is returned may still be NULL, which
* typically results from joining two exits and NULLing
* the pointer to a node in the middle of the queue, which
* is easier than trying to completely yank the node from
* the middle of the queue
*
****************************************************************************/
EXITS *dequeue_exit()
{
EXITS *ext;
QUEUE *que;
--gc;
if (!debugging) {
printf("%4d", gc);
++gcnum;
if (gcnum > 15) {
gcnum = 0;
printf("\n");
}
}
/* make sure there is an exit queue node */
if (queuehead == NULL)
fatal_error("dequeue_exit(): attempt to dequeue from empty queue");
ext = queuehead->exit;
que = queuehead;
if (queuehead->queuenext == NULL)
queuehead = queuetail = NULL;
else
queuehead = queuehead->queuenext;
free_queue_node(que);
return (ext);
}
/****************************************************************************
*
* add_location_to_queue() - add a location to the list of locations
*
****************************************************************************/
add_location_to_queue(ptr)
LOCAT *ptr;
{
if (firstlocation == NULL)
firstlocation = lastlocation = ptr;
else {
ptr->next = NULL;
ptr->previous = lastlocation;
lastlocation->next = ptr;
lastlocation = ptr;
}
}
/****************************************************************************
*
* fatal_error() - print out a fatal error message and abort execution
*
****************************************************************************/
fatal_error(strg, arg)
char strg[], arg[];
{
printf("Error: ");
printf(strg, arg);
printf("\n\n");
exit(1);
}